package com.shkco.xbjcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.shkco.exception.GlobalCustomException;
import com.shkco.exception.GlobalExceptionCode;
import com.shkco.result.JSONResult;
import com.shkco.utils.AssertUtil;
import com.shkco.xbjcc.domain.AlipayInfo;
import com.shkco.xbjcc.domain.PayFlow;
import com.shkco.xbjcc.domain.PayOrder;
import com.shkco.xbjcc.dto.AlipayNotifyDto;
import com.shkco.xbjcc.dto.PayNotifyLocalTxParamDto;
import com.shkco.xbjcc.dto.PayParamDto;
import com.shkco.xbjcc.service.IAlipayInfoService;
import com.shkco.xbjcc.service.IPayOrderService;
import com.shkco.xbjcc.service.IPayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

@Service
@Slf4j
public class PayServiceImpl implements IPayService {

    @Autowired
    private IPayOrderService payOrderService;

    @Autowired
    private IAlipayInfoService alipayInfoService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public JSONResult checkPayOrder(String orderNo) {
        return payOrderService.selectByOrderNo(orderNo) != null ? JSONResult.success():JSONResult.error();
    }

    @Override
    public String apply(PayParamDto payParamDto) {
        log.info("用户发起支付,订单号:{}",payParamDto.getOrderNo());
        PayOrder payOrder = payOrderService.selectByOrderNo(payParamDto.getOrderNo());
        AssertUtil.isNotNull(payOrder, GlobalExceptionCode.COURSE_ORDER_INEXISTENCE);
        boolean boo = payOrder.getPayStatus() == PayOrder.TO_BE_PAID;
        AssertUtil.isTrue(boo,GlobalExceptionCode.COURSE_ORDER_PROCESSED);
        if (payOrder.getPayType() == 1){
            log.info("开始调用支付宝支付服务");
            return aliPay(payOrder,payParamDto.getCallUrl());
        }
        return null;
    }

    @Override
    public String alipayNotify(AlipayNotifyDto alipayNotifyDto) {
        try {
            Map map = JSON.parseObject(JSON.toJSONString(alipayNotifyDto), Map.class);
            // 校验签名
            Boolean isSign = Factory.Payment.Common().verifyNotify(map);
            if (!isSign){
                return "success";
            }
            // 判断支付状态
            if (!alipayNotifyDto.getTrade_status().equals(AlipayNotifyDto.TRADE_SUCCESS)){
                return "success";
            }
            // 根据订单编号判断支付单是否存在
            PayOrder payOrder = payOrderService.selectByOrderNo(alipayNotifyDto.getOut_trade_no());
            if (payOrder == null){
                return "success";
            }

            // 判断用户支付的金额是否正确
            if (payOrder.getAmount().compareTo(new BigDecimal(alipayNotifyDto.getTotal_amount())) != 0){
                return "success";
            }

            // 判断支付单状态
            if (PayOrder.PAYMENT_CANCELLATION.equals(payOrder.getPayStatus()) || PayOrder.PAYMENT_FAILURE.equals(payOrder.getPayStatus())){

                try {
                    // 进行退款
                    AlipayTradeRefundResponse result = Factory.Payment.Common().refund(alipayNotifyDto.getOut_trade_no(), alipayNotifyDto.getTotal_amount());
                    if (!result.getCode().equals("10000")){

                    }else {
                        return "success";
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            if (PayOrder.PAYMENT_SUCCESSFUL.equals(payOrder.getPayStatus())){
                return "success";
            }

            PayNotifyLocalTxParamDto payNotifyLocalTxParamDto = getPayNotifyLocalTxParamDto(alipayNotifyDto, payOrder);

            String arg = JSON.toJSONString(payNotifyLocalTxParamDto);
            String extParams = payOrder.getExtParams();
            Message<String> message = MessageBuilder.withPayload(extParams).build();
            // 发送mq事务消息
            TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
                    "tx-pay-notify-group",
                    "tx-pay-notify-topic:tx-pay-notify-tag",
                    message,
                    arg);
            // 判断发送消息是否失败
            boolean sendOk = result.getSendStatus() == SendStatus.SEND_OK;
            if (!sendOk){
                return "fail";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
        return "success";

    }

    private PayNotifyLocalTxParamDto getPayNotifyLocalTxParamDto(AlipayNotifyDto alipayNotifyDto, PayOrder payOrder) throws ParseException {
        try {
            payOrder.setPayStatus(PayOrder.PAYMENT_SUCCESSFUL);
            payOrder.setUpdateTime(new Date());
            PayFlow payFlow = new PayFlow();
            payFlow.setCode(alipayNotifyDto.getCode());
            payFlow.setMsg(alipayNotifyDto.getMsg());
            SimpleDateFormat simpleFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date parse = simpleFormatter.parse(alipayNotifyDto.getNotify_time());
            payFlow.setNotifyTime(parse);
            payFlow.setOutTradeNo(alipayNotifyDto.getOut_trade_no());
            payFlow.setPassbackParams(alipayNotifyDto.getPassback_params());
            payFlow.setPaySuccess(true);
            payFlow.setSubject(alipayNotifyDto.getSubject());
            payFlow.setResultDesc(alipayNotifyDto.getBody());
            payFlow.setTotalAmount(new BigDecimal(alipayNotifyDto.getTotal_amount()));
            payFlow.setTradeStatus(alipayNotifyDto.getTrade_status());
            PayNotifyLocalTxParamDto payNotifyLocalTxParamDto = new PayNotifyLocalTxParamDto(payOrder, payFlow);
            return payNotifyLocalTxParamDto;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String aliPay(PayOrder payOrder, String callUrl) {
        AlipayInfo alipayInfo = alipayInfoService.selectOne(null);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(alipayInfo));
        try {
            // 2. 发起API调用（以创建当面付收款二维码为例）
            AlipayTradePagePayResponse response = Factory.Payment.Page().pay(payOrder.getSubject(),payOrder.getOrderNo(),payOrder.getAmount().toString(),callUrl);
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                log.info("调用支付宝支付服务成功");
                return response.getBody();
            }
        } catch (Exception e) {
            log.info("调用遭遇异常，原因：" + e.getMessage());
            throw new GlobalCustomException(GlobalExceptionCode.ALIPAY_PAY_FAILURE);
        }
        return null;
    }

    private static Config getOptions(AlipayInfo alipayInfo) {

        Config config = new Config();
        config.protocol = alipayInfo.getProtocol();
        config.gatewayHost = alipayInfo.getGatewayHost();
        config.signType = alipayInfo.getSignType();
        config.appId = alipayInfo.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = alipayInfo.getMerchantPrivateKey();
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = alipayInfo.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = alipayInfo.getNotifyUrl();
        return config;
    }
}
